﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
using log4net;

namespace Entangle
{
    class ActivityMonitor
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(ActivityMonitor));

        [DllImport("User32.dll")]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        internal struct LASTINPUTINFO
        {
            public uint cbSize;
            public uint dwTime;
        }

        private int _idleTreshold;
        private uint _lastActivity;
        private bool _enabled;
        private ActivityState _state = ActivityState.Unknown;

        private LASTINPUTINFO lastInput;
        private DateTime _lastReset;
        private Timer activityCheckerTimer;
        private Stopwatch activityStopWatch = new Stopwatch();

        #region "Events"
        [Description("Occurs when the user becomes active.")]
        public event EventHandler UserActiveEvent;

        [Description("Occurs when the user becomes idle.")]
        public event EventHandler UserIdleEvent;
        #endregion
        public ActivityMonitor()
            : this(60000, false)
        {}
        public ActivityMonitor(int threshold)
            : this(threshold, false)
        {}
        public ActivityMonitor(int treshold, bool enable)
        {
            log.Info("Creating ActivityMonitor, treshold=" + treshold + ",enable=" + enable);
            this._idleTreshold = treshold;

            this.lastInput = new LASTINPUTINFO();
            this.lastInput.cbSize = (uint)Marshal.SizeOf(this.lastInput);

            GetLastInputInfo(ref this.lastInput);
            this._lastActivity = this.lastInput.dwTime;

            activityCheckerTimer = new Timer(new TimerCallback(this.GetLastInput), null, Timeout.Infinite, 1000);
            this._lastReset = DateTime.Now;
            this.Enabled = enable;
            log.Info("ActivityMonitor created.");
        }

        public TimeSpan Idle
        {
            get
            {
                return TimeSpan.FromTicks(DateTime.Now.Ticks - this._lastActivity);
            }
        }

        private void GetLastInput(object userState)
        {
                try
                {
                    GetLastInputInfo(ref this.lastInput);
                    this._lastActivity = this.lastInput.dwTime;
                    if ((Environment.TickCount - this._lastActivity) > this._idleTreshold)
                    {
                        if (this._state != ActivityState.Inactive)
                        {
                            this._state = ActivityState.Inactive;
                            this.RaiseUserIdleEvent();
                        }
                    }
                    else if (this._state != ActivityState.Active)
                    {
                        this._state = ActivityState.Active;
                        this.RaiseUserActiveEvent();
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Error geting last user input.", ex);
                    throw ex;
                }
        }

        /// <summary>
        /// Performs appropriate error-checking before raising the UserActive event
        /// </summary>
        private void RaiseUserActiveEvent()
        {
            if (this.UserActiveEvent != null)
            {
                this.UserActiveEvent(this, null);
            }
        }

        /// <summary>
        /// Performs appropriate error-checking before raising the UserIdle event
        /// </summary>
        private void RaiseUserIdleEvent()
        {
            if (this.UserIdleEvent != null)
            {
                this.UserIdleEvent(this, null);
            }
        }

        public bool Enabled
        {
            get { return this._enabled; }
            set
            {
                if (value)
                    Enable();
                else
                    Disable();
            }
        }
        public void Enable()
        {
            if (!this._enabled)
            {
                this.activityCheckerTimer.Change(0, 1000);
                this._enabled = true;
                this.activityStopWatch.Start();
            }
        }
        public void Disable()
        {
            if (this._enabled)
            {
                this.activityCheckerTimer.Change(Timeout.Infinite, Timeout.Infinite);
                this._enabled = false;
                this._state = ActivityState.Unknown;
                this.activityStopWatch.Stop();
            }
        }
    }

}
